package com.zzk.jpmvvmbase.network.interceptor.logging

import android.text.TextUtils
import com.blankj.utilcode.util.LogUtils
import com.zzk.jpmvvmbase.network.interceptor.logging.LogInterceptor.Companion.isJson
import com.zzk.jpmvvmbase.network.interceptor.logging.LogInterceptor.Companion.isXml
import com.zzk.jpmvvmbase.util.CharacterHandler.Companion.jsonFormat
import com.zzk.jpmvvmbase.util.CharacterHandler.Companion.xmlFormat
import okhttp3.MediaType
import okhttp3.Request

/**
 * @ProjectName: JetPackMVVMBase
 * @Package: com.zzk.jpmvvmbase
 * @ClassName:
 * @Description:
 * @Author: brilliantzhao
 * @CreateDate: 2021.1.19 14:14
 * @UpdateUser:
 * @UpdateDate: 2021.1.19 14:14
 * @UpdateRemark:
 * @Version: 1.0.0
 */
class DefaultFormatPrinter : FormatPrinter {
    /**
     * 打印网络请求信息, 当网络请求时 {[okhttp3.RequestBody]} 可以解析的情况
     *
     * @param request
     * @param bodyString
     */
    override fun printJsonRequest(
        request: Request,
        bodyString: String
    ) {
        val requestBody = BODY_TAG + bodyString
        LogUtils.d(
            getTag(true),
            URL_TAG + request.url() + N +
                    getRequest(request) + N +
                    requestBody
        )
    }

    /**
     * 打印网络请求信息, 当网络请求时 {[okhttp3.RequestBody]} 为 `null` 或不可解析的情况
     *
     * @param request
     */
    override fun printFileRequest(request: Request) {
        LogUtils.d(
            getTag(true),
            URL_TAG + request.url() + N +
                    getRequest(request)
        )
    }

    /**
     * 打印网络响应信息, 当网络响应时 {[okhttp3.ResponseBody]} 可以解析的情况
     *
     * @param chainMs      服务器响应耗时(单位毫秒)
     * @param isSuccessful 请求是否成功
     * @param code         响应码
     * @param headers      请求头
     * @param contentType  服务器返回数据的数据类型
     * @param bodyString   服务器返回的数据(已解析)
     * @param segments     域名后面的资源地址
     * @param message      响应信息
     * @param responseUrl  请求地址
     */
    override fun printJsonResponse(
        chainMs: Long,
        isSuccessful: Boolean,
        code: Int,
        headers: String,
        contentType: MediaType?,
        bodyString: String?,
        segments: List<String?>,
        message: String,
        responseUrl: String
    ) {
        var bodyString = bodyString
        bodyString =
            when {
                isJson(contentType) -> jsonFormat(bodyString!!)
                isXml(
                    contentType
                ) -> xmlFormat(bodyString)
                else -> bodyString
            }
        val responseBody = BODY_TAG + bodyString
        val urlLine = URL_TAG + responseUrl + N
        LogUtils.d(
            getTag(false),
            urlLine +
                    getResponse(
                        headers,
                        chainMs,
                        code,
                        isSuccessful,
                        segments,
                        message
                    ) + responseBody
        )
    }

    /**
     * 打印网络响应信息, 当网络响应时 {[okhttp3.ResponseBody]} 为 `null` 或不可解析的情况
     *
     * @param chainMs      服务器响应耗时(单位毫秒)
     * @param isSuccessful 请求是否成功
     * @param code         响应码
     * @param headers      请求头
     * @param segments     域名后面的资源地址
     * @param message      响应信息
     * @param responseUrl  请求地址
     */
    override fun printFileResponse(
        chainMs: Long,
        isSuccessful: Boolean,
        code: Int,
        headers: String,
        segments: List<String?>,
        message: String,
        responseUrl: String
    ) {
        val urlLine = URL_TAG + responseUrl + N
        LogUtils.d(
            getTag(false),
            urlLine +
                    getResponse(
                        headers,
                        chainMs,
                        code,
                        isSuccessful,
                        segments,
                        message
                    )
        )
    }

    companion object {
        private const val TAG = "HttpLog"
        private val LINE_SEPARATOR = System.getProperty("line.separator")
        private const val N = "\n"
        private const val T = "\t"
        private const val BODY_TAG = "Body:"
        private const val URL_TAG = "URL: "
        private const val METHOD_TAG = "Method: @"
        private const val HEADERS_TAG = "Headers:"
        private const val STATUS_CODE_TAG = "Status Code: "
        private const val RECEIVED_TAG = "Received in: "
        private const val CORNER_UP = "┌ "
        private const val CORNER_BOTTOM = "└ "
        private const val CENTER_LINE = "├ "
        private const val DEFAULT_LINE = "│ "

        private fun isEmpty(line: String): Boolean {
            return TextUtils.isEmpty(line) || N == line || T == line || TextUtils.isEmpty(
                line.trim { it <= ' ' }
            )
        }

        /**
         *
         */
        private fun getRequest(request: Request): String {
            val log: String
            val header = request.headers().toString()
            log =
                METHOD_TAG + request.method() + "\n" +
                        if (isEmpty(header)) "" else HEADERS_TAG + dotHeaders(header)
            return log
        }

        /**
         *
         */
        private fun getResponse(
            headers: String,
            tookMs: Long,
            code: Int,
            isSuccessful: Boolean,
            segments: List<String?>,
            message: String
        ): String {
            val segmentString = slashSegments(segments)
            val headersStr = if (isEmpty(headers)) "" else HEADERS_TAG + dotHeaders(headers)
            val tookMsStr = RECEIVED_TAG + tookMs + N
            val codeStr = STATUS_CODE_TAG + code + N
            val isSuccessfulStr = "isSuccessful: $isSuccessful$N"
            val segmentsStr =
                "segments: ${if (!TextUtils.isEmpty(segmentString)) "$segmentString$N" else ""}"
            val messageStr = "message: $message$N"
            return headersStr + tookMsStr + codeStr + isSuccessfulStr + segmentsStr + messageStr
        }

        /**
         *
         */
        private fun slashSegments(segments: List<String?>): String {
            val segmentString = StringBuilder()
            for (segment in segments) {
                segmentString.append("/").append(segment)
            }
            return segmentString.toString()
        }

        /**
         * 对 `header` 按规定的格式进行处理
         *
         * @param header
         * @return
         */
        private fun dotHeaders(header: String): String {
            val headers = header.split(LINE_SEPARATOR!!).toTypedArray()
            val builder = StringBuilder()
            var tag = "─ "
            if (headers.size > 1) {
                for (i in headers.indices) {
                    tag = if (i == 0) {
                        CORNER_UP
                    } else if (i == headers.size - 1) {
                        CORNER_BOTTOM
                    } else {
                        CENTER_LINE
                    }
                    builder.append(tag).append(headers[i]).append("\n")
                }
            } else {
                for (item in headers) {
                    builder.append(tag).append(item).append("\n")
                }
            }
            return builder.toString()
        }

        /**
         *
         */
        private fun getTag(isRequest: Boolean): String {
            return if (isRequest) {
                "$TAG-Request"
            } else {
                "$TAG-Response"
            }
        }
    }
}